home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 201-225 / 201 / draco / doc / changes.doc next >
Text File  |  1995-03-13  |  19KB  |  432 lines

  1.         Changes in V1.2 of the Amiga Draco System    Jan 28,1989
  2.  
  3.  
  4. I. Language Changes
  5.  
  6.  
  7.     1. Floating Point
  8.  
  9.     The Draco compiler now supports a floating point type. This type,
  10.     called 'float', is the Amiga's IEEE double precision floating point
  11.     package, as available in V1.3 of the Amiga Operating System. The older
  12.     V1.2 library can be used, but it is slower, does not provide the
  13.     trancendental functions, and does not support a math coprocessor. See
  14.     the accompanying writeup, 'float.doc' for a detailed description of
  15.     Draco's floating point support.
  16.  
  17.  
  18.     2. New Types
  19.  
  20.     Two new built-in types have been added to the language. They are
  21.     'arbptr' and 'boid'. The first is similar in nature to ANSI C's
  22.     'void *' type, in that it is compatible with all pointer types. In the
  23.     new include files, all memory allocation/deallocation/copying routines,
  24.     along with the DOS routines 'Read' and 'Write' have been declared to
  25.     use 'arbptr' parameters/results. For example, Exec's memory allocation
  26.     and deallocation functions are declared as:
  27.  
  28.     extern AllocMem(ulong length, flags)arbptr;
  29.     extern FreeMem(arbptr region; ulong length)void;
  30.  
  31.     This allows usage such as this:
  32.  
  33.     proc test()void:
  34.         *char charBuffer;
  35.         *ulong longBuffer;
  36.         *externType xBuffer;
  37.  
  38.         charBuffer := AllocMem(200, 0);
  39.         longBuffer := AllocMem(100 * sizeof(ulong), MEMF_PUBLIC);
  40.         xBuffer := AllocMem(sizeof(externType) * 2, MEMF_CLEAR);
  41.  
  42.         ...
  43.  
  44.         FreeMem(xBuffer, sizeof(externType) * 2);
  45.         FreeMem(longBuffer, 100 * sizeof(ulong));
  46.         FreeMem(charBuffer, 200);
  47.     corp;
  48.  
  49.     In the old Draco, all 6 calls would have required a 'pretend'.
  50.  
  51.     Type 'boid' can only be used as the result type of a procedure. This
  52.     type indicates that the procedure returns a boolean result, but that it
  53.     is OK to ignore the result. This is merely making available to the
  54.     programmer what the compiler already handled internally as the type
  55.     returned by the I/O statements ('read', 'readln', 'write', 'writeln').
  56.     A good example of this is the DOS function 'DeleteFile', which now
  57.     returns a 'boid'. The actual result is 'false' if the file didn't
  58.     exists, or couldn't be destroyed for some other reason. Many
  59.     programmers are happy to ignore that result. Without the use of 'boid',
  60.     it would have been necessary to 'pretend' the result to 'void'.
  61.  
  62.  
  63.     3. New Construct
  64.  
  65.     A new construct has been added to the compiler. This is the 'ignore'
  66.     statement. This statement consists of the reserved word 'ignore'
  67.     followed by an expression. The expression is evaluated as normal, and
  68.     the result is then discarded. This is entirely equivalent to
  69.     'pretend'ing something to 'void', but is much less ugly. A common
  70.     example is that of ignoring the result of 'Write', e.g.
  71.  
  72.     ignore Write(Output(), "Help! I'm dying!!\n", 18);
  73.  
  74.  
  75.     4. Register Variables
  76.  
  77.     The concept of user declared variables that live in machine registers
  78.     (a concept used in most 'C' compilers) has been added to Draco. This is
  79.     a way for a simple compiler (such as Draco sort-of is) to produce
  80.     significantly better machine code than is otherwise possible. The
  81.     syntax in Draco is simple - merely add the reserved word 'register' to
  82.     the front of a declaration - all variables declared in that declaration
  83.     will be allocated registers, if possible. The only types that Draco
  84.     will put in registers are 'byte', integral types, enumeration types and
  85.     pointer types. It is an error to attempt to put any other type into a
  86.     register. If there are not enough machine registers for all of the
  87.     variables declared as 'register', some will be allocated to storage
  88.     instead of to registers. The current compiler allocates registers in
  89.     the order that the variables are encountered, but I'm not sure if I
  90.     should make that part of the language specification. Note that
  91.     procedure parameters can be placed in registers as well as local
  92.     variables. See the discussion below on code generation for an example
  93.     of the effect of register variables.
  94.  
  95.  
  96. II. Library Changes
  97.  
  98.  
  99.     1. Compatibility
  100.  
  101.     Except for changes discussed here, the new Draco libraries are source
  102.     level compatible with the old libraries. This means that a program
  103.     which compiled and ran with the old Draco system should compile and run
  104.     with the new Draco system. The ENTIRE program should be recompiled and
  105.     linked, however. All of the internal library routine names have been
  106.     changed to begin with '_d_'. This was done to ease porting of the Draco
  107.     system to a UNIX machine, where some runtime system routine names
  108.     clashed with standard UNIX system and library routine names. When
  109.     switching to the new Draco system, make sure you replace all of the
  110.     library files (in 'drlib:') when you replace the compiler.
  111.  
  112.  
  113.     2. New Functions
  114.  
  115.     My memory may be off here, but I think the new functions added since
  116.     the previous release are:
  117.  
  118.     ConvTime - convert a second counter to a string
  119.     GetCurrentTime - return a second counter of the current time/date
  120.     LineFlush - flush the standard output buffer (useful when printing
  121.         things like dots to show progress). This routine is called
  122.         internally whenever a read from standard input is done.
  123.  
  124.  
  125.     3. Changed Functions
  126.  
  127.     The following functions have been renamed:
  128.  
  129.     BlockMove => BlockCopy
  130.     BlockMoveB => BlockCopyB
  131.  
  132.     These names are more descriptive, especially to those unfamiliar with
  133.     how computers work.
  134.  
  135.  
  136.     4. Use With Pipes
  137.  
  138.     The pipe: device supplied with AmigaDOS V1.3 is a bit finicky about the
  139.     modes in which it is opened, whereas the rest of the system doesn't
  140.     much care. To allow pipe: access to work properly inside the Draco I/O
  141.     library, the following is done:
  142.  
  143.     if the file name starts with "pipe:" (case significant), then
  144.         if the open is for read, use MODE_OLDFILE
  145.         else use MODE_NEWFILE
  146.     else
  147.         use MODE_READWRITE
  148.  
  149.     This seems to do what is desired, but is sort of kludgy.
  150.  
  151.  
  152.     5. Include Files
  153.  
  154.     There have been a few minor changes to the include files, some
  155.     mentioned above. There are also one or two new ones.
  156.  
  157.     The include files supplied with this release are minimally compressed.
  158.     This is done using 'Ded', the accompanying Draco editor. The scheme
  159.     simply encodes sequences of blanks as a single byte with the high order
  160.     bit set and the low order 7 bits encoding the blank count. They can be
  161.     viewed normally using 'Ded', or you can write a simple program to
  162.     expand them.
  163.  
  164.  
  165. III. Compiler Changes
  166.  
  167.     1. Optimization
  168.  
  169.     Much improved code generation is the most significant feature of this
  170.     new compiler. Generated code is now roughly on par with that generated
  171.     by Lattice C V4.0 . The major improvements are:
  172.  
  173.     register variables - saves code space in that no 16 bit offset is
  174.         needed to access the variable. Speed is gained by saving two or
  175.         more memory cycles on the access. The current compiler can
  176.         allocate 3 address registers and 4 data registers for register
  177.         variables. I will close my eyes here and mention a kludge that
  178.         you can do to sort-of get more register variables:
  179.  
  180.         register *char p, q, r;
  181.         register *uint up @ p, uq @ q, ur @ r;
  182.  
  183.         Don't blame me if you use this and your program breaks because
  184.         you are trying to store two values in one register!
  185.  
  186.     register tracking - this technique consists of keeping track of
  187.         what values are in all of the non-allocated registers, and not
  188.         reloading a value if it is already in a register. In the
  189.         current compiler, this is not perfect, but it does gain quite a
  190.         bit, especially for heavily used pointer variables. For
  191.         example, the source
  192.  
  193.         *struct {int f1, f2, f3; long f4, f5; bool f6} p;
  194.  
  195.         p*.f1 := 0;
  196.         p*.f2 := 8;
  197.         p*.f3 := 200;
  198.         p*.f4 := 0x12345678;
  199.         p*.f5 := 1;
  200.         p*.f6 := true;
  201.  
  202.         would generate something like:
  203.  
  204.         MOVE.L    xx(A6),A5
  205.         CLR.W    (A5)
  206.         MOVE.W    #8,2(A5)
  207.         MOVE.W    #200,4(A5)
  208.         MOVE.L    #0x12345678,6(A5)
  209.         MOVEW    #1,D7
  210.         MOVE.L    D7,10(A5)
  211.         MOVE.B    #1,14(A5)
  212.  
  213.         Six loads of 'p' into a register, a total of 24 bytes of code,
  214.         have been saved. Declaring 'p' as a register variable would
  215.         save an additional 4 bytes (the first MOVE). Note also that
  216.         special casing has used a MOVEQ/MOVE.L pair to store '-1' into
  217.         field 'f5'. This is shorter by 2 bytes and slightly faster.
  218.         Unfortunately, it also illustrates one of the pitfalls of
  219.         trying to optimize without full knowledge of the entire
  220.         function - it uses register D7, which must therefore be saved
  221.         and restored in the entry/exit sequences; thus if the above
  222.         code is the entire content of a function, the total code
  223.         generated is actually increased by the 'optimization' of using
  224.         the MOVEQ! (It just occurred to me that this is a prime
  225.         candidate for the peephole optimizer - make the pair use D0,
  226.         which is scratch, instead of D7. - done!)
  227.  
  228.     peephole optimizer - this optimizer is so named because it looks at
  229.         the generated code though a small peephole - any short
  230.         (typically two or three instructions) code sequence that can
  231.         better be done some other way is re-arranged to that better
  232.         way. This is done without any global knowledge of what is going
  233.         on, so it must preserve the full meaning of the sequence. An
  234.         exception to the full meaning preservation is that a value in a
  235.         temporary register need not be preserved, so long as the global
  236.         information of what is in all of the temporary registers is
  237.         updated. Typical 'peepholes' are:
  238.  
  239.         MOVE.W    A,Dx
  240.         ADDQ.W    #y,Dx    =>  ADDQ.W  #y,A
  241.         MOVE.W    Dx,A
  242.  
  243.         MOVE.B    (A5),D7 =>  MOVE.B (A5),(A4)
  244.         MOVE.B    D7,(A4)
  245.         MOVE.L    A5,A3
  246.         ADDQ.L    #1,A3    =>  ADDQ.L #1,A5    => MOVE.B (A5)+,(A4)+
  247.         MOVE.L    A3,A5
  248.         MOVE.L    A4,A3
  249.         ADDQ.L    #1,A3    =>  ADDQ.L #1,A4
  250.         MOVE.L    A3,A4
  251.  
  252.         MOVE.L    X,Y    =>  MOVE.L X,Dz
  253.         MOVE.L    X,Dz        MOVE.L Dz,Y
  254.  
  255.         Note that the second is actually a combination of 4 peepholes,
  256.         yielding a dramatic improvement. Also, the third doesn't
  257.         actually reduce the number of instructions, but it saves 2 or 4
  258.         bytes (depending on whether X is local or global) and saves 1
  259.         or 2 memory references.
  260.  
  261.     improved entry/exit sequences - the compiler no longer saves and
  262.         restores all registers for each procedure - it only saves and
  263.         restores those that are actually used in the procedure. The
  264.         ultimate of this is no save/restore at all. This saves
  265.         execution time and execution stack space.
  266.  
  267.     improved code sequences in a number of places - the most notable
  268.         here is the code for the 'for' loop, which, in the best of
  269.         cases, can turn into a single 'DBF' instruction. This happens
  270.         for a 16 bit variable counting down by 1 to 0.
  271.  
  272.     branch shortening - the 68000 family of processors have two lengths
  273.         of conditional and unconditional PC-relative branch
  274.         instructions. The short ones are 16 bits long and can branch
  275.         plus or minus 127 bytes from the current position. The long
  276.         ones are 32 bits and can branch plus or minus 32K bytes. It is
  277.         desireable to use the short forms wherever possible. The Draco
  278.         compiler has always done this for backwards branches, but it is
  279.         considerably harder for forward ones, since when the branch is
  280.         generated, the target location is not known. The new compiler
  281.         will use short branches in many cases, by the laborious method
  282.         of keeping track of them all, and shuffling code around as
  283.         necessary. This also requires fixing up the tables of
  284.         relocation entries, external references, and constant uses.
  285.  
  286.     These optimizations, especially peepholing and branch shortening, can
  287.     be quite time consuming to perform. The compiler, being written in
  288.     itself however, gains speed from the very optimizations that slow it
  289.     down. I haven't kept track of the actual changes in compile time, but
  290.     the compiler can still trot along at a couple of thousand lines per
  291.     minute, and compiles considerably faster than Lattice C V4.0 .
  292.  
  293.     Even though the compiler is now much better at generating code for
  294.     whatever source you give it, there is still lots of opportunities for
  295.     the programmer to "hand-optimize" his source for even better
  296.     performance. A good knowledge of the 68000 architecture comes in handy
  297.     here. Also handy is an understanding of how Draco's code generation
  298.     works. This is a bit hard to come by, but some insights can be obtained
  299.     by trying various things and examining the generated code. As an
  300.     example, try the two variants of the "Sieve of Eratosthenes" program
  301.     which are included on this disk.
  302.  
  303.  
  304.     2. Frame Pointer
  305.  
  306.     Code generated by the new Draco compiler now references all local
  307.     variables and parameters via an offset from A6, the frame pointer. The
  308.     previous compiler went directly from A7, the stack pointer, and thus
  309.     had A6 available for other use. This change was made for two reasons.
  310.     One is that it allowed the above-mentioned improvements in entry/exit
  311.     sequences. The second is that it makes using a debugger quite a bit
  312.     easier. The unfortunate aspect is that of decreasing by 1 the number of
  313.     address registers available for other use. This was partially
  314.     compensated for by the use of A1, which is not normally possible
  315.     because it is a scratch register and is not saved through a function
  316.     call. This was fixed by having the calling function save and restore it
  317.     around the call if it is in use.
  318.  
  319.  
  320.     3. Operator Types
  321.  
  322.     Operator types are now fully implemented in the Amiga Draco compiler.
  323.     For a complete example of using them, see the files 'complex.g',
  324.     'complex.d', and 'complexTest.d' on this disk.
  325.  
  326.     4. Other
  327.  
  328.     There are three command-line flags that are now supported by the
  329.     compiler. None are of great significance to most people. '-v' turns on
  330.     some verbose output that shows how the optimizations are going on a
  331.     function-by-function basis. '-s' produces a summary of this information
  332.     at the end of each source file. '-d' tells the compiler to emit minimal
  333.     symbolic information for use with a debugger. All that is currently
  334.     emitted is a chunk naming the procedures. This is useful with the
  335.     Metascope debugger, but is equivalent to what their 'Def' program does.
  336.  
  337.     The compiler can now be interrupted using CONTROL-C. It will exit
  338.     cleanly, closing all files it has open. The check is made only when I/O
  339.     is done, so you may have to wait a second or two. Do NOT try to
  340.     interrupt the supplied version of 'Blink' this way, since it tends to
  341.     crash your machine when you do that.
  342.  
  343.     Some internal coding changes, coupled with the entry/exit improvements
  344.     mentioned previously, make the compiler require considerably less stack
  345.     space than before. I haven't done any experiments, but I suspect that
  346.     10K of stack is enough to compile most programs.
  347.  
  348.     Considerable effort has gone into making the compiler robust. This
  349.     means that it is very unlikely to get into an infinite loop or crash
  350.     your machine. There are still a few ways in which you can confuse it,
  351.     however, most having to do with register handling. The compiler now has
  352.     some internal consistency checks which should catch these and abort
  353.     cleanly. Please let me know, sending an example source file, if the
  354.     compiler ever aborts without first producing at least one error
  355.     message. At one point I was trying to get the compiler to never trigger
  356.     any of the consistency checks, but it soon became clear that doing so
  357.     would add greatly to the code bulk of the compiler, and probably slow
  358.     it down, so there are still a few error recovery situations where it
  359.     will get confused and abort.
  360.  
  361.  
  362. IV. Things Still To Do
  363.  
  364.     A compiler is never really completed. There are always more things that
  365.     can be done to it, not the least of which is totally rewriting it. The
  366.     Draco compiler most definitely needs a total rewrite, but it is more
  367.     likely to be replaced by a totally new compiler. There are still many
  368.     lesser changes that would be nice to do. Some of them are:
  369.  
  370.     - more peephole optimizations - there are always more of these that can
  371.     be done - a perusal of any reasonable sized program will always
  372.     find code sequences that could be done better. They payoff of doing
  373.     more would be very slim, however, and each such addition adds to
  374.     the size of the compiler and slows it down a little. The point
  375.     where an optimization could pay for itself by improving the
  376.     compiler's own code has long since been passed.
  377.  
  378.     - better register tracking - the compiler currently forgets the values
  379.     of all temporary registers on any branch or merge point. This is
  380.     overkill, and a more intelligent merging scheme could gain
  381.     considerable efficiency in non-registerized programs. Doing this is
  382.     a fair amount of work, however. A simple form of common
  383.     subexpression evaluation could also be obtained by remembering
  384.     slightly more about what is in the registers.
  385.  
  386.     - some changes in the rules for I/O would be nice. E.g.
  387.     - allow a length specification on input through character pointers,
  388.         so that no overflowing of the buffer can occur
  389.     - allow output of pointers and floating point values in hexadecimal
  390.     - allow 'e', 'f', and 'g' formats for operator types - this would
  391.         allow better control of complex number output
  392.  
  393.     - redo the I/O library internals so that '\e' and '\(0xff)' can be
  394.     passed through on text I/O through channels (this involves having
  395.     the bottom-level character-at-a-time routines return a 'uint' with
  396.     a couple of special values, rather than a 'char' with a couple of
  397.     special values)
  398.  
  399.     - add library functions to allow explicit setting of the standard input
  400.     and output channels
  401.  
  402.     - take the branch tables produced for the 'case' construct out of line.
  403.     They are currently in-line and can confuse disassemblers and
  404.     debuggers. Constant displays have already been taken out of line in
  405.     the new compiler
  406.  
  407.     - allow fully nested constant displays. Currently a constant display
  408.     can directly contain another constant display, but cannot contain a
  409.     pointer to another one. The most useful form would be pointers to
  410.     strings.
  411.  
  412.     - support > 32767 bytes of local variables. The internal code for
  413.     accessing such variables already exists; what is lacking is the
  414.     proper special-cases of entry/exit code, combined with the
  415.     optimizations that are done on entry/exit code.
  416.  
  417.     - some mechanism to allow Draco code to access external symbols defined
  418.     by other languages (e.g. C or assembler) would be useful. This
  419.     would probabably be a declaration followed or preceeded by a
  420.     keyword and an external-name string.
  421.  
  422.     - global variables initialized at compile time would be useful. These
  423.     would normally be file-level variables which are shared by several
  424.     routines and consist of tables of strings, numbers, etc.
  425.  
  426.     - the peephole optimizations are quite bulky in the way they test for
  427.     applicability. Some scheme for simplifying the tests would be nice.
  428.     If nothing else can be done, some re-arranging of the code can get
  429.     rid of some duplication. I don't want to do this until I'm fairly
  430.     sure the peepholer is stable, since doing so would impair its
  431.     readability.
  432.